﻿using System.Windows;
using System.Linq;
using Microsoft.Phone.Scheduler;
using Microsoft.Phone.Shell;
using System.Diagnostics;
using CCWP7.Model;
using System;
using System.Threading;
using System.Collections.Generic;
using Microsoft.Phone.Net.NetworkInformation;

namespace CCWP7.STA
{
    public class ScheduledAgent : ScheduledTaskAgent
    {
        const string STR_ProjectPagexamlProjectId = "/ProjectPage.xaml?ProjectId=";
        private static volatile bool _classInitialized;

        /// <remarks>
        /// ScheduledAgent constructor, initializes the UnhandledException handler
        /// </remarks>
        public ScheduledAgent()
        {
            if (!_classInitialized)
            {
                _classInitialized = true;
                // Subscribe to the managed exception handler
                Deployment.Current.Dispatcher.BeginInvoke(() => Application.Current.UnhandledException += ScheduledAgent_UnhandledException);
            }
        }

        /// Code to execute on Unhandled Exceptions
        private void ScheduledAgent_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // An unhandled exception has occurred; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        /// <summary>
        /// Agent that runs a scheduled task
        /// </summary>
        /// <param name="task">
        /// The invoked task
        /// </param>
        /// <remarks>
        /// This method is called when a periodic or resource intensive task is invoked
        /// </remarks>
        protected override void OnInvoke(ScheduledTask task)
        {
            var start = DateTime.Now;

            try
            {
                if (NetworkInterface.GetIsNetworkAvailable())
                {
                    var sdm = new ServerDataManager();
                    sdm.Load();

                    var pairs = sdm.Servers.Select(server =>
                                new ServerUpdatePair
                                {
                                    Server = server,
                                    Event = new ManualResetEvent(false),
                                })
                                .ToArray();

                    for (int i = 0; i < pairs.Length; i++)
                        ReadAndUpdateServer(sdm, pairs[i]);

                    var list = pairs.ToList();

                    while (list.Count > 0)
                    {
                        var events = list.Select(l => l.Event).ToArray();

                        Debug.WriteLine("Waiting for: " + list.Count);

                        var index = WaitHandle.WaitAny(events);

                        if (index >= 0)
                        {
                            Debug.WriteLine("Updating: " + list[index].Server.Uri);

                            UpdateServerTile(list[index].Server);

                            list.RemoveAt(index);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }

            Debug.WriteLine("OnInvoke complete: {0}", DateTime.Now - start);

            NotifyComplete();
        }

        void ReadAndUpdateServer(ServerDataManager sdm, ServerUpdatePair pair)
        {
            sdm.ReadAndUpdateServer(pair.Server, s =>
            {
                Debug.WriteLine("Read: " + pair.Server.Uri);

                var ret = pair.Event.Set();

                Debug.WriteLine("Event Set: " + ret);
            });
        }

        struct ServerUpdatePair
        {
            public Server Server { get; set; }
            public ManualResetEvent Event { get; set; }
        }

        void UpdateServerTile(Server server)
        {
            try
            {
                if (server.Report != null && server.Report.Projects != null)
                {
                    var projects = server.Report.Projects;

                    foreach (var at in ShellTile.ActiveTiles)
                        Debug.WriteLine(at.NavigationUri.ToString());

                    var q = from at in ShellTile.ActiveTiles
                            let uri = at.NavigationUri.ToString()
                            where uri.StartsWith(STR_ProjectPagexamlProjectId)
                            let id = ParseProjectId(uri)
                            join p in projects on id equals p.GetHashCode()
                            select new { Project = p, Tile = at };

                    q = q.ToArray();

                    var service = new ShellTileDataService() as IShellTileDataService;
                    //TODO: Add code to perform your task in background
                    foreach (var at in q)
                    {
                        var data = service.GetShellTile(at.Project);

                        Debug.WriteLine("Updating({0}, with {1})", at.Tile.NavigationUri, data);

                        at.Tile.Update(data);
                    }
                }

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
        }

        static int ParseProjectId(string uri)
        {
            var id = int.Parse(uri.Substring(STR_ProjectPagexamlProjectId.Length));

            Debug.WriteLine(id.ToString());

            return id;
        }
    }
}